package cn.com.myspring.framework.webmvc.servlet;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.com.myspring.framework.annotation.MYController;
import cn.com.myspring.framework.annotation.MYRequestMapping;
import cn.com.myspring.framework.context.MYApplicationContext;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MYDispatcherServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    private MYApplicationContext context;

    private List<MYHandlerMapping> handlerMappings = new ArrayList<MYHandlerMapping>();

    private Map<MYHandlerMapping, MYHandlerAdapter> handlerAdapters = new HashMap<MYHandlerMapping, MYHandlerAdapter>();

    private List<MYViewResolver> viewResolvers = new ArrayList<MYViewResolver>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 1、初始化ApplicationContext
        context = new MYApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        // 2、初始化Spring MVC 九大组件
        initStrategies(context);
    }

    // 初始化策略
    protected void initStrategies(MYApplicationContext context) {
        // 多文件上传的组件
        initMultipartResolver(context);
        // 初始化本地语言环境
        initLocaleResolver(context);
        // 初始化模板处理器
        initThemeResolver(context);
        
        // 初始话handlerMapping，必须实现
        initHandlerMappings(context);
        // 初始化参数适配器，必须实现
        initHandlerAdapters(context);
        
        // 初始化异常拦截器
        initHandlerExceptionResolvers(context);
        // 初始化视图预处理器
        initRequestToViewNameTranslator(context);
        // 初始化视图转换器，必须实现
        initViewResolvers(context);
        // 参数缓存器
        initFlashMapManager(context);
    }

    private void initMultipartResolver(MYApplicationContext context) {
    }

    private void initLocaleResolver(MYApplicationContext context) {
    }

    private void initThemeResolver(MYApplicationContext context) {
    }

    // 初始化HandlerMapping
    private void initHandlerMappings(MYApplicationContext context) {
    
        String[] beanNames = context.getBeanDefinitionNames();
    
        try {
            // 遍历beans,获取 url-controller-method 的映射
            for (String beanName : beanNames) {
    
                Object controller = context.getBean(beanName);
    
                Class<?> clazz = controller.getClass();
    
                if (!clazz.isAnnotationPresent(MYController.class)) {
                    continue;
                }
    
                String baseUrl = "";
                // 获取Controller的url配置
                if (clazz.isAnnotationPresent(MYRequestMapping.class)) {
                    MYRequestMapping requestMapping = clazz.getAnnotation(MYRequestMapping.class);
                    baseUrl = requestMapping.value();
                }
    
                // 获取Method的url配置
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
    
                    // 没有加RequestMapping注解的直接忽略
                    if (!method.isAnnotationPresent(MYRequestMapping.class)) {
                        continue;
                    }
    
                    // 映射URL
                    MYRequestMapping requestMapping = method.getAnnotation(MYRequestMapping.class);
    
                    // 拼接成正则表达
                    String regex = ("/" + baseUrl + "/" + requestMapping.value().replaceAll("\\*", ".*"))
                            .replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);
    
                    this.handlerMappings.add(new MYHandlerMapping(pattern, controller, method));
                    log.info("Mapped " + regex + "," + method);
                }
    
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    
    }

    private void initHandlerAdapters(MYApplicationContext context) {
    
        // HandlerAdapter为HandlerMapping的参数转换器,request中的参数,转换成HandlerMapping中方法上的形参
        // 根据handlerMapping初始化HandlerAdapter
        for (MYHandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new MYHandlerAdapter());
        }
    
    }

    private void initHandlerExceptionResolvers(MYApplicationContext context) {
    }

    private void initRequestToViewNameTranslator(MYApplicationContext context) {
    }

    private void initViewResolvers(MYApplicationContext context) {
    
        // 拿到模板的存放目录
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();
    
        File templateRootDir = new File(templateRootPath);
        // 获取模板列表
        String[] templates = templateRootDir.list();
        for (int i = 0; i < templates.length; i++) {
            this.viewResolvers.add(new MYViewResolver(templateRoot));
        }
    
    }

    private void initFlashMapManager(MYApplicationContext context) {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req, resp);
        } catch (Exception e) {
            try {
                Map<String,Object> map = new HashMap<String, Object>();
                map.put("detail", e.getCause().getMessage());
                map.put("stackTrace", Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
                processDispatchResult(req, resp, new MYModelAndView("500",map));
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 1、通过从request中拿到URL，去匹配一个HandlerMapping
        MYHandlerMapping handler = getHandler(req);

        if (handler == null) {
            processDispatchResult(req, resp, new MYModelAndView("404"));
            return;
        }

        // 2、准备调用前的参数
        MYHandlerAdapter ha = getHandlerAdapter(handler);

        // 3、真正的调用方法,返回ModelAndView存储了要穿页面上值，和页面模板的名称
        MYModelAndView mv = ha.handle(req, resp, handler);

        // 这一步才是真正的输出
        processDispatchResult(req, resp, mv);

    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, MYModelAndView mv)
            throws Exception {
        // 把给我的ModleAndView变成一个HTML、OuputStream、json、freemark、veolcity
        // ContextType
        if (null == mv) {
            return;
        }

        // 如果ModelAndView不为null，怎么办？
        if (this.viewResolvers.isEmpty()) {
            return;
        }

        for (MYViewResolver viewResolver : this.viewResolvers) {
            MYView view = viewResolver.resolveViewName(mv.getViewName(), null);
            view.render(mv.getModel(), req, resp);
            return;
        }

    }

    private MYHandlerAdapter getHandlerAdapter(MYHandlerMapping handler) {
        if (this.handlerAdapters.isEmpty()) {
            return null;
        }
        MYHandlerAdapter ha = this.handlerAdapters.get(handler);
        if (ha.supports(handler)) {
            return ha;
        }
        return null;
    }

    private MYHandlerMapping getHandler(HttpServletRequest req) throws Exception {
        if (this.handlerMappings.isEmpty()) {
            return null;
        }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for (MYHandlerMapping handler : this.handlerMappings) {
            try {
                Matcher matcher = handler.getPattern().matcher(url);
                // 如果没有匹配上继续下一个匹配
                if (!matcher.matches()) {
                    continue;
                }

                return handler;
            } catch (Exception e) {
                throw e;
            }
        }
        return null;
    }

}
